﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.GACManagedAccess;
using PS.WSSDeploy.Contract;

namespace PS.WSSDeploy.Service.Tools
{
    public class TempEntry
    {
        public string AssemblyPath { get; set; } 
        public string PdbPath { get; set; } 
    }

    public class AssemblyInstaller
    {
        private Dictionary<string, TempEntry> TempEntries { get; set; }
        private string TempDir { get; set; }


        public AssemblyInstaller()
        {
            TempEntries = new Dictionary<string, TempEntry>();
            TempDir = Path.GetDirectoryName(Path.GetTempFileName()); 
        }

        public void Install(IEnumerable<WSSFile> entries)
        {
            try
            {
                InitTempEntries(entries);
                DeleteTempFiles();
                CreateTempFiles(entries);
                CopyToGAC();
            }
            finally
            {
                DeleteTempFiles();
            }
        }

        private void InitTempEntries(IEnumerable<WSSFile> entries)
        {
            foreach (WSSFile entry in entries)
            {
                string filePath = Path.Combine(TempDir, entry.Name);
                string keyName = Path.GetFileNameWithoutExtension(filePath);
                string fileExt = Path.GetExtension(filePath);

                if (string.IsNullOrEmpty(keyName))
                {
                    throw new ArgumentException("File name is null or empty.");
                }

                if (fileExt != ".exe" && fileExt != ".dll" && fileExt != ".pdb")
                {
                    //throw new ArgumentException("Bad file extension.");
                    continue;
                }

                if (TempEntries.ContainsKey(keyName))
                {
                    if (fileExt == ".exe" || fileExt == ".dll")
                    {
                        TempEntries[keyName].AssemblyPath = filePath;
                    }
                    else if (fileExt == ".pdb")
                    {
                        TempEntries[keyName].PdbPath = filePath;
                    }
                }
                else
                {
                    TempEntry tempEntry = new TempEntry();
                    if (fileExt == ".exe" || fileExt == ".dll")
                    {
                        tempEntry.AssemblyPath = filePath;
                    }
                    else if (fileExt == ".pdb")
                    {
                        tempEntry.PdbPath = filePath;
                    }
                    TempEntries.Add(keyName, tempEntry);
                }
            }
        }

        private void CreateTempFiles(IEnumerable<WSSFile> entries)
        {
            foreach (WSSFile entry in entries)
            {
                string fileName = entry.Name;
                byte[] fileContent = entry.Content;
                string filePath = Path.Combine(TempDir, fileName);
                File.WriteAllBytes(filePath, fileContent);
            }
        }

        private void DeleteTempFiles()
        {
            foreach (KeyValuePair<string, TempEntry> kvp in TempEntries)
            {
                TempEntry te = kvp.Value;
                if (!string.IsNullOrEmpty(te.AssemblyPath))
                {
                    if (File.Exists(te.AssemblyPath))
                    {
                        File.Delete(te.AssemblyPath);
                    }
                }
                if (!string.IsNullOrEmpty(te.PdbPath))
                {
                    if (File.Exists(te.PdbPath))
                    {
                        File.Delete(te.PdbPath);
                    }
                }
            }
        }

        private void CopyToGAC()
        {
            foreach (KeyValuePair<string, TempEntry> kvp in TempEntries)
            {
                string currentPath = null;
                try
                {
                    TempEntry te = kvp.Value;
                    if (!string.IsNullOrEmpty(te.AssemblyPath))
                    {
                        currentPath = te.AssemblyPath;
                        AssemblyCache.InstallAssembly(te.AssemblyPath, null, AssemblyCommitFlags.Force);
                        if (!string.IsNullOrEmpty(te.PdbPath))
                        {
                            currentPath = te.PdbPath;
                            string assemblyName = kvp.Key;
                            string assemblyPath = AssemblyCache.QueryAssemblyInfo(assemblyName);
                            string assemblyDir = Path.GetDirectoryName(assemblyPath);
                            string pdbName = Path.GetFileName(te.PdbPath);
                            File.Copy(te.PdbPath, Path.Combine(assemblyDir, pdbName), true);
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new Win32Exception(
                        string.Format("'{0}': {1}", Path.GetFileName(currentPath), ex.Message));
                }
            }
        }
    }
}
